﻿#region Imports
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using CommonUtilities.Attributes;
using CommonUtilities.Controls;
using CommonUtilities.Enums;
using CommonUtilities.Exceptions;
using CommonUtilities.Extensions;
using CommonUtilities.Info;
using Microsoft.Office.Core;
#endregion Imports

namespace CommonUtilities.Util
{
    public static class Utilities
    {
        #region Declarations
        #region Constants
        /// <summary>
        /// Delimeter for various func. &apos;|&apos;
        /// </summary>
        public const char cDelim = '|';

        /// <summary>
        /// Delimeter for CSV &apos;,&apos;
        /// </summary>
        public const char cCSVDelim = ',';

        /// <summary>
        /// Mask for CSV delimeter &apos;%2C&apos;
        /// </summary>
        public const string sCSVMask = "%2C";

        public static readonly Dictionary<string, string> dictXMLMask
            = new Dictionary<string, string>()
                .AddToDictionary("&", "&amp;")
                .AddToDictionary("<", "&lt;")
                .AddToDictionary(">", "&gt;")
                .AddToDictionary("'", "&apos;")
                .AddToDictionary("\"", "&quot;");

        /// <summary>
        /// New-line character &apos;\r\n&apos;
        /// </summary>
        public const string cNewLine = "\r\n";

        /// <summary>
        /// Default alignment for Left
        /// </summary>
        public const ContentAlignment DefaultAlignmentLeft = ContentAlignment.MiddleLeft;

        /// <summary>
        /// Default alignment for Right
        /// </summary>
        public const ContentAlignment DefaultAlignmentRight = ContentAlignment.MiddleRight;

        /// <summary>
        /// Default file encoding
        /// </summary>
        public readonly static Encoding DefaultEncoding = Encoding.Default; // ANSI; Unicode

        /// <summary>
        /// Gets a value that indicates whether a debugger is attached to the process
        /// </summary>
        /// <value>TRUE if a debugger is attached; otherwise, FALSE</value>
        public readonly static bool IsDebug = Debugger.IsAttached;
        #endregion Constants
        #endregion Declarations

        #region Properties
        /// <summary>
        /// List of files/directories restricted for browsing
        /// </summary>
        public static List<string> ForbiddenDirectories { get; set; }

        /// <summary>
        /// Default culture for the currently running thread
        /// </summary>
        public static CultureInfo DefaultCulture { get { return Thread.CurrentThread.CurrentCulture; } }
        #endregion Properties

        #region Methods
        #region Path/Directory/Files
        /// <summary>
        /// Gets the names of subdirectories in the specified directory
        /// </summary>
        /// <param name="path">Path for which an array of subdirectory names is returned</param>
        /// <returns>A string array of the names of subdirectories in specified directory</returns>
        /// <exception cref="T:System.UnauthorizedAccessException">The caller does not have the required permission to <paramref name="path"/></exception>
        /// <exception cref="T:System.ArgumentException">path is a zero-length string, contains only white space, or contains one
        ///     or more invalid characters as defined by <see cref="T:System.IO.Path.InvalidPathChars"/>.</exception>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="path"/> is <code>null</code>.</exception>
        /// <exception cref="T:System.IO.IOException"><paramref name="path"/> is a file name.</exception>
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified <paramref name="path"/> is invalid (for example, it is on an unmapped drive).</exception>
        /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum
        ///     length. For example, on Windows-based platforms, paths must be less than
        ///     248 characters and file names must be less than 260 characters.</exception>
        public static string[] GetValidDirectories(string path)
        {
            PopulateForbiddenDirectories();
            return path.ValidatePath(ePathValidation.Directory)
                ? Directory.GetDirectories(path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar.ToString())
                    .Where(dir => !ForbiddenDirectories.Contains(dir.GetDirectoryNameOnly())).ToArray()
                : new string[] { };
        }

        /// <summary>
        /// Gets the names of files in the specified directory
        /// <param name="path">Path of directory to retrieve files from</param>
        /// <returns>A string array of file names in the specified directory</returns>
        /// <exception cref="T:System.ArgumentException"><paramref name="path"/> is  is a zero-length string, contains only white space, or contains one</para>
        ///     or more invalid characters as defined by <code>System.IO.Path.InvalidPathChars</code>.</exception>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="path"/> is <code>null</code>.</exception>
        /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both 
        ///     exceed the system-defined maximum length. For example, on Windows-based platforms, paths 
        ///     must be less than 248 characters and file names must be less than 260 characters.</exception>
        /// <exception cref="T:System.IO.IOException"><paramref name="path"/> is a file name. -or- 
        ///     a network error has occurred.</para></exception>
        /// <exception cref="T:System.UnauthorizedAccessException">The caller does not have the required permission to <paramref name="path"/></exception>
        /// <exception cref="T:System.ArgumentException">path is a zero-length string, contains only white space, or contains one
        ///     or more invalid characters as defined by System.IO.Path.InvalidPathChars.</exception>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="path"/> is<code>null</code>.</exception>
        /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum
        ///     length. For example, on Windows-based platforms, paths must be less than
        ///     248 characters and file names must be less than 260 characters.</exception>
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid (for example, it is on an unmapped drive).</exception>
        public static string[] GetValidFiles(string path)
        {
            PopulateForbiddenDirectories();
            return path.ValidatePath(ePathValidation.Directory)
                ? Directory.GetFiles(path).Where(dir => !ForbiddenDirectories.Contains(Path.GetFileName(dir)) && Path.HasExtension(dir)).ToArray()
                : new string[] { };
        }

        /// <summary>
        /// Populates files/directories restricted for browsing
        /// </summary>
        private static void PopulateForbiddenDirectories()
        {
            List<string> forbiddenDirectories = new List<string>();
            forbiddenDirectories.Add("System Volume Information");
            forbiddenDirectories.Add("RECYCLER");
            forbiddenDirectories.Add("$RECYCLE.BIN");
            forbiddenDirectories.Add("Thumbs.db");
            forbiddenDirectories.Add("Desktop.ini");
            forbiddenDirectories.Add("Autorun.inf");
            ForbiddenDirectories = forbiddenDirectories;
        }

        /// <summary>
        /// Creates file at the specified path
        /// </summary>
        /// <param name="filePath">Path of the file</param>
        public static void CreateFile(string filePath)
        {
            if (!filePath.IsPathFormatValid())
                throw new InvalidOperationException("Not a valid file name.");

            string dirPath = filePath.GetDirectoryNameFull();
            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);

            FileStream fs = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
            fs.Flush();
            fs.Close();
            fs.Dispose();
        }

        #region File Read/Write
        #region CSV
        /// <summary>
        /// Reads CSV into DataTable from specified path and delimeter
        /// </summary>
        /// <param name="path">Path of CSV file</param>
        /// <param name="delimeter">Delimeter for CSV (default: comma &apos;,&apos;)</param>
        /// <param name="tableName">Table name of DataTable</param>
        /// <returns></returns>
        public static DataTable ReadCSV(string path, char delimeter = cCSVDelim, string tableName = "")
        {
            DataTable dtResult = new DataTable(tableName);
            string[] csvLines = File.ReadAllLines(path);

            dtResult = ReadCSV(csvLines, delimeter, tableName);
            return dtResult;
        }

        /// <summary>
        /// Reads CSV into DataTable from list of CSV rows and delimeter
        /// </summary>
        /// <param name="csvLines">Collection of CSV rows</param>
        /// <param name="delimeter">Delimeter for CSV (default: comma &apos;,&apos;)</param>
        /// <param name="tableName">Table name of DataTable</param>
        /// <returns></returns>
        public static DataTable ReadCSV(string[] csvLines, char delimeter = cCSVDelim, string tableName = "")
        {
            if (tableName.IsBlank()) tableName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
            DataTable dtResult = new DataTable(tableName);
            csvLines = csvLines.RemoveEmptyLines().ToArray();

            if (csvLines.IsLinqIterable<string>(true))
            {
                #region :: CASE-1
                string header = csvLines.First();

                // Get headers
                List<DataColumn> dcCSV = header.Trim(delimeter).Split(delimeter)
                    .Select(col => new DataColumn((delimeter == cCSVDelim) ? col.RestoreCSVChars() : col)).ToList();

                // Add columns
                dcCSV.ForEach(col => dtResult.Columns.Add(col));

                // Add rows
                foreach (string ln in csvLines.Skip(1))
                {
                    DataRow drCSV = dtResult.NewRow();
                    dcCSV.ForEach(col =>
                    {
                        if (!ln.IsBlank() && ln != "\0")
                        {
                            string val = ln.Split(delimeter).ElementAt(dcCSV.ToList().IndexOf(col));
                            drCSV[col] = (delimeter == cCSVDelim) ? val.RestoreCSVChars() : val;
                        }
                    });

                    dtResult.Rows.Add(drCSV);
                }
                #endregion :: CASE-1

                #region :: CASE-2
                //bool isFirstRowHeader = true;
                //string header2 = isFirstRowHeader ? "Yes" : "No";
                //
                //string pathOnly = Path.GetDirectoryName(path);
                //string fileName = Path.GetFileName(path);
                //
                //string sql = @"SELECT * FROM [" + fileName + "]";
                //using (OleDbConnection connection = new OleDbConnection(
                //          @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly +
                //          ";Extended Properties=\"Text;HDR=" + header2 + "\""))
                //
                //using (OleDbCommand command = new OleDbCommand(sql, connection))
                //using (OleDbDataAdapter adapter = new OleDbDataAdapter(command))
                //{
                //    dtCSV.Locale = CultureInfo.CurrentCulture;
                //    adapter.Fill(dtCSV);
                //} 
                #endregion :: CASE-2
            }

            return dtResult;
        }

        /// <summary>
        /// Reads CSV into DataTable from specified path and delimeter
        /// </summary>
        /// <param name="csvLines">Collection of CSV rows</param>
        /// <param name="delimeter">Delimeter for CSV (default: comma &apos;,&apos;)</param>
        /// <param name="tableName">Table name of DataTable</param>
        /// <returns></returns>

        /// <summary>
        /// Reads CSV
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="path"></param>
        /// <param name="dictDataImportName"></param>
        /// <param name="delimeter"></param>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public static IEnumerable<TSource> ReadCSV<TSource>(string path, Dictionary<string, string> dictDataImportName, char delimeter = cCSVDelim, string tableName = "")
            where TSource : class,new()
        {
            ObservableCollection<TSource> dtResult = new ObservableCollection<TSource>();

            if (dictDataImportName.IsNull()) dictDataImportName = AttributeLists<TSource>.PropertyMapDataImportName;
            DataTable dtSeriesPathMap = ReadCSV(path, delimeter, tableName);

            foreach (DataRow drSeriesPathMap in dtSeriesPathMap.Rows)
            {
                TSource item = new TSource { };
                dtSeriesPathMap.Columns._Cast<DataColumn>().ForEach(hdr =>
                {
                    // RKD : correct indexing in case of import name mismatch
                    PropertyInfo prop = typeof(TSource).GetProperty(dictDataImportName.GetKeyFromValue<string, string>(hdr.ColumnName, true, false));
                    if (!prop.IsNull()) prop.SetValue(item, ChangeDatatype(drSeriesPathMap[hdr.ColumnName], prop.PropertyType), null);
                });

                dtResult.Add(item);
            }

            return dtResult;
        }

        /// <summary>
        /// Changes the type of data while populating episode info
        /// </summary>
        /// <param name="data">Episode info in flat format</param>
        /// <param name="typ">Type of the property to be populated</param>
        /// <returns></returns>
        private static object ChangeDatatype(object data, Type typ)
        {
            try
            {
                if (typ == typeof(Int32))
                    return Convert.ToInt32(data);
                else if (typ == typeof(String))
                    return Convert.ToString(data);
                else if (typ == typeof(DateTime))
                    return Convert.ToDateTime(data.ToString().IsBlank() ? DateTime.MinValue : data);
                else
                    return data;
            }
            catch (Exception) { if (typ == typeof(DateTime)) return DateTime.MinValue; }

            return null;
        }
        #endregion CSV

        #region XML
        /// <summary>
        /// Reads data from XML
        /// </summary>
        /// <typeparam name="T">Output type</typeparam>
        /// <param name="ignoreNodes">Nodes to ignore</param>
        /// <param name="basePath"></param>
        /// <param name="xmlName"></param>
        /// <param name="xmlExtn"></param>
        /// <returns></returns>
        public static IEnumerable<TOutput> ReadFromXML<TOutput>(List<string> ignoreNodes, string basePath, string xmlName, string xmlExtn = ".xml") where TOutput : new()
        {
            ObservableCollection<TOutput> res = new ObservableCollection<TOutput>();
            if (xmlExtn.IsNullOrEmpty()) xmlExtn = ".xml";
            string xmlPath = basePath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(xmlName) + '.' + xmlExtn.Trim('.');

            if (!basePath.IsNullOrEmpty() && !basePath.IsNullOrEmpty()
                && xmlPath.ValidatePath(ePathValidation.File))
            {
                // Read from XML :: http://support.microsoft.com/kb/307548
                XmlTextReader reader = new XmlTextReader(xmlPath);
                string sXML = ""; string sJson = "";
                PropertyInfo prop = null; TOutput t = new TOutput { }; string nodeName = "";
                string className = Path.GetExtension(typeof(TOutput).ToString()).Trim('.'); // typeof( T).GetType().ToString()
                ignoreNodes = new List<string>();
                ignoreNodes.AddRange(new string[] { "ArrayOf" + className });
                while (reader.Read())
                {
                    nodeName = reader.Name;
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.XmlDeclaration:
                            sXML += ("<?" + nodeName + " " + reader.Value + "?>" + Utilities.cNewLine);
                            break;

                        case XmlNodeType.Element: // The node is an element.
                            //nodeName = reader.Name;
                            if (!ignoreNodes.Contains(nodeName))
                            {
                                sXML += "<" + nodeName + ">" + (nodeName == className ? Utilities.cNewLine : "");
                                //Console.Write("<" + reader.Name);
                                //Console.WriteLine(">");

                                if (nodeName == className)
                                {
                                    t = new TOutput { };
                                    //sJson += ("{\"" + nodeName + "\" : {");
                                    sJson += ("{"); ;
                                }
                                else
                                {
                                    prop = t.GetType().GetProperty(nodeName);
                                    sJson += ("\"" + nodeName + "\" : ");
                                }
                            }

                            break;

                        case XmlNodeType.Text: //Display the text in each element.
                            if (!ignoreNodes.Contains(nodeName))
                            {
                                sXML += reader.Value;
                                //Console.WriteLine (reader.Value);

                                object obj /*= Utilities.ToType<>(reader.Value, prop.PropertyType);*/
                                    = Convert.ChangeType(reader.Value, prop.PropertyType);
                                if (nodeName != className)
                                {
                                    prop.SetValue(t, obj, null);
                                    sJson += ("\"" + reader.Value + "\", ");
                                }
                            }

                            break;

                        case XmlNodeType.EndElement: //Display the end of the element.
                            //nodeName = reader.Name;
                            if (!ignoreNodes.Contains(nodeName))
                            {
                                sXML += "</" + reader.Name + ">" + Utilities.cNewLine;
                                //Console.Write("</" + reader.Name);
                                //Console.WriteLine(">");

                                if (nodeName == className)
                                {
                                    //sJson = sJson.Trim().TrimEnd(',') + "}}, ";
                                    sJson = sJson.Trim().TrimEnd(',') + "}, ";
                                }
                                else
                                {
                                    res.Add(t);
                                    sJson += "";
                                }
                            }

                            break;

                        default:
                            sXML += reader.Value;
                            break;
                    }
                }

                sXML = File.ReadAllText(xmlPath, Utilities.DefaultEncoding);
                sJson = "[" + sJson.Trim().TrimEnd(',') + "]";
                res = sJson.DeSerializeFromFormattedData<ObservableCollection<TOutput>>(eSerializationFormat.JSon);
                res = sXML.DeSerializeFromFormattedData<ObservableCollection<TOutput>>(eSerializationFormat.Xml);
                MessageBox.Show(sXML, "XML");
                MessageBox.Show(sJson, "JSON");

                return res.AsEnumerable();
            }

            return null;
        }

        public static IEnumerable<TOutput> ReadFromXML<TOutput>(string basePath, string xmlName, string xmlExtn = ".xml") where TOutput : new()
        {
            ObservableCollection<TOutput> res = new ObservableCollection<TOutput>();
            if (xmlExtn.IsNullOrEmpty()) xmlExtn = ".xml";
            string xmlPath = basePath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(xmlName) + '.' + xmlExtn.Trim('.');

            if (!basePath.IsNullOrEmpty() && !basePath.IsNullOrEmpty()
                && xmlPath.ValidatePath(ePathValidation.File))
            {
                string sXML = "";
                PropertyInfo prop = null; TOutput t = new TOutput { };
                string className = Path.GetExtension(typeof(TOutput).ToString()).Trim('.'); // typeof( T).GetType().ToString()

                sXML = File.ReadAllText(xmlPath, Utilities.DefaultEncoding);
                res = sXML.DeSerializeFromFormattedData<ObservableCollection<TOutput>>(eSerializationFormat.Xml);

                return res.AsEnumerable();
            }

            return null;
        }
        #endregion XML
        #endregion File Read/Write
        #endregion Path/Directory/Files

        #region Applications
        /// <summary>
        /// Opens a path based on the specified viewer
        /// </summary>
        /// <param name="path">Path/File to open</param>
        /// <param name="openingApp">Application to open with</param>
        public static void OpenPath(string path, eOpeningApp openingApp = eOpeningApp.Default)
        {
            if (!path.IsNullOrEmpty())
            {
                if (path.ValidatePath(ePathValidation.File)
                    || (openingApp == eOpeningApp.Default && path.ValidatePath(ePathValidation.Directory)))
                {
                    if (!ValidateOpeningApp(openingApp, path))
                        openingApp = eOpeningApp.Default;

                    Process.Start(GetApplication(openingApp), path);
                }
            }
        }

        private static void OpenSpecialApp(string path, eOpeningApp openingApp)
        {
            switch (openingApp)
            {
                case eOpeningApp.Default:
                    break;
                case eOpeningApp.MSExcel:
                    break;
                case eOpeningApp.MSWord:
                    // CASE-1 ::
                    //Microsoft.Office.Interop.Word.Application ap = new Microsoft.Office.Interop.Word.Application();
                    //Document doc = ap.Documents.Open(path);
                    //doc.Activate();

                    // CASE-2 ::
                    object wordObject = null;
                    try
                    { wordObject = Marshal.GetActiveObject("Word.Application"); }
                    catch (Exception) { /* Do nothing.*/ }

                    Microsoft.Office.Interop.Word.Application word = null;
                    bool wordInstanceCreated = false;
                    if (!wordObject.IsNull())
                        word = (Microsoft.Office.Interop.Word.Application)wordObject;
                    else
                    {
                        wordInstanceCreated = true;
                        word = new Microsoft.Office.Interop.Word.Application();
                    }

                    word.Visible = true;

                    object missing = Missing.Value;
                    object fullFilePath = path;
                    //object encoding = WdSaveFormat.wdFormatRTF; // http://msdn.microsoft.com/en-us/library/microsoft.office.tools.word.document.saveas(v=vs.80).aspx
                    object encoding = MsoEncoding.msoEncodingUTF8;
                    object noEncodingDialog = true; // http://msdn.microsoft.com/en-us/library/bb216319(office.12).aspx
                    word.Documents.Open(ref fullFilePath, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref encoding, ref missing, ref missing, ref missing, ref noEncodingDialog, ref missing);

                    break;
                case eOpeningApp.Notepad:
                    break;
                case eOpeningApp.PDFReader:
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Validates opening application
        /// </summary>
        /// <param name="openingApp">Application to open with</param>
        /// <param name="path">Path/File to oepn</param>
        /// <returns>TRUE: if folder or file with allowed extensions</returns>
        private static bool ValidateOpeningApp(eOpeningApp openingApp, string path)
        {
            string extn = Path.GetExtension(path).ToLower().Trim('.');
            if (!extn.IsNullOrEmpty() && openingApp != eOpeningApp.Default)
                return GetAllowedExtn(openingApp).Replace(',', cDelim).ToLower().Split(cDelim).Contains(extn);
            else return true;
        }

        /// <summary>
        /// Fetches the allowed extn. against opening apps
        /// </summary>
        /// <param name="openingApp">Application to open with</param>
        /// <returns>Allowed extn. (comma-separated)</returns>
        private static string GetAllowedExtn(eOpeningApp openingApp)
        {
            switch (openingApp)
            {
                case eOpeningApp.MSExcel:
                    return "xls,xlsx,csv,xlsm";
                case eOpeningApp.MSWord:
                    return "doc,docx,rtf";
                case eOpeningApp.Notepad:
                    return "txt,text";
                case eOpeningApp.PDFReader:
                    return "pdf";
                case eOpeningApp.Default:
                default:
                    return "";
            }
        }

        /// <summary>
        /// Fetches process name of the application against OpeningApp enum
        /// </summary>
        /// <param name="openingApp">Application to open with</param>
        /// <returns>Process name of an application</returns>
        private static string GetApplication(eOpeningApp openingApp)
        {
            switch (openingApp)
            {
                case eOpeningApp.Default:
                    return "explorer.exe";
                case eOpeningApp.MSExcel:
                    return "excel.exe";
                case eOpeningApp.Notepad:
                    return "notepad.exe";
                case eOpeningApp.MSWord:
                //    return "winword.exe";
                case eOpeningApp.PDFReader: // RKD: disabled all other apps; takes Windows default viewer
                //    return ??
                default:
                    return "explorer.exe";
            }
        }
        #endregion Applications

        #region Validations
        /// <summary>
        /// Validates if specified value falls between specified range
        /// </summary>
        /// <param name="value">Value to validate</param>
        /// <param name="lower">Lower margin</param>
        /// <param name="upper">Upper margin</param>
        /// <param name="includeEnds">TRUE: includes both margins</param>
        /// <returns></returns>
        public static bool ValidateBetween(double value, double lower, double upper, bool includeEnds = true)
        {
            if (lower > upper) SwapValues(lower, upper);

            if (includeEnds)
                return value >= lower && value <= upper;
            else
                return value > lower && value < upper;
        }

        /// <summary>
        /// Compares values specified in &apos;source&apos; parameter against &apos;reference&apos; parameter
        /// <para>(Calculation: &apos;<paramref name="source"/>&apos; &lt;<paramref name="comparer"/>&gt; &apos;<paramref name="reference"/>&apos;)</para>
        /// </summary>
        /// <param name="source">The value to be compared</param>
        /// <param name="reference">The value to compare against</param>
        /// <param name="comparer">Comparison type</param>
        /// <returns></returns>
        public static bool CompareValues(long source, long reference, eComparers comparer)
        {
            switch (comparer)
            {
                case eComparers.GreaterThan:
                    return source > reference;
                case eComparers.LesserThan:
                    return source < reference;
                case eComparers.GreaterThanOrEquals:
                    return source >= reference;
                case eComparers.LesserThanOrEquals:
                    return source <= reference;
                case eComparers.NotEquals:
                    return source != reference;
                case eComparers.NotGreaterThan:
                    return !(source > reference);
                case eComparers.NotLesserThan:
                    return !(source < reference);
                case eComparers.NotGreaterThanOrEquals:
                    return !(source >= reference);
                case eComparers.NotLesserThanOrEquals:
                    return !(source <= reference);
                case eComparers.Equals:
                    return source == reference;
                default:
                    return false;
            }
        }
        #endregion Validations

        #region Enums
        /// <summary>
        /// Gets enum from control name after replacing specified part
        /// </summary>
        /// <typeparam name="T">Enum to be converted into</typeparam>
        /// <param name="ctrlControl">Control</param>
        /// <param name="remove">Part of text to be removed</param>
        /// <returns>Enum corresponding to specified control name</returns>
        public static T GetEnumFromControl<T>(object ctrlControl, params string[] remove)
        {
            string enumValue = GetControlName(ctrlControl).RemoveText(remove);
            return GetEnumFromText<T>(enumValue);
        }

        /// <summary>
        /// Gets enum from enum name
        /// </summary>
        /// <typeparam name="T">Enum to be converted into</typeparam>
        /// <param name="enumText">Enum value (name as string)</param>
        /// <returns>Enum corresponding specified text</returns>
        public static T GetEnumFromText<T>(string enumText)
        {
            return Enum.GetValues(
                typeof(T)).Cast<T>().Where<T>(enm => enm.ToString() == enumText).First();
        }
        #endregion Enums

        #region Properties
        public static T CopyValues<T>(T src, T trgt)
            where T : class, new()
        {
            trgt = new T { };

            // serialize
            foreach (PropertyInfo prop in src.GetType().GetProperties())
            {
                object val = prop.GetValue(src);

                bool throwException = false;
                if (HasProperty<T>(prop.Name, throwException) && CanWrite<T>(prop.Name, throwException))
                    trgt.GetType().GetProperty(prop.Name).SetValue(trgt, val, null);
                else if (!CanWrite<T>(prop.Name, throwException))
                { int i = 0; }
            }

            return trgt;
        }

        public static TOut CopyValues<TIn, TOut>(TIn src, TOut trgt)
            where TIn : class, new()
            where TOut : class, new()
        {
            if (trgt.IsNull()) trgt = new TOut { };

            TOut tmp = CopyValues<TIn, TOut>(src);
            return CopyValues<TOut>(tmp, trgt);
        }

        public static TOut CopyValues<TIn, TOut>(TIn src)
            where TIn : class, new()
            where TOut : class, new()
        {
            TOut trgt = new TOut { };

            // serialize
            foreach (PropertyInfo prop in src.GetType().GetProperties())
            {
                object val = prop.GetValue(src);

                bool throwException = false;
                if (HasProperty<TOut>(prop.Name, throwException) && CanWrite<TOut>(prop.Name, throwException))
                    trgt.GetType().GetProperty(prop.Name).SetValue(trgt, val, null);
                else if (!CanWrite<TOut>(prop.Name, throwException))
                { int i = 0; }
            }

            return trgt;
        }

        /// <summary>
        /// RKD
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <param name="trgt"></param>
        /// <returns></returns>
        /// <seealso cref="http://stackoverflow.com/questions/209160/nullable-type-as-a-generic-parameter-possible" />
        /// <seealso cref="http://stackoverflow.com/questions/1878712/generics-and-nullable-type" />
        /// <seealso cref="http://stackoverflow.com/questions/1344898/implementing-nullable-types-in-generic-interface" />
        public static void CopyValues<T>(IEnumerable<T> src, ref IEnumerable<T> trgt)
            where T : class, new()
        {
            ObservableCollection<T> trgt_obs = new ObservableCollection<T>();

            if (src.IsLinqIterable<T>(true))
            {
                foreach (T src_item in src)
                {
                    T cpy_item = default(T); // new T { };
                    trgt_obs.Add(CopyValues<T>(src_item, cpy_item));
                }
            }

            trgt = trgt_obs.AsEnumerable();
        }

        public static IEnumerable<T> CopyValues<T>(IEnumerable<T> src)
            where T : class, new()
        {
            IEnumerable<T> trgt = null;
            CopyValues<T>(src, ref trgt);
            return trgt;
        }

        public static bool HasProperty<T>(string propName, bool throwException)
        {
            try { if (!typeof(T).UnderlyingSystemType.GetProperties().Where(prop => prop.Name == propName).FirstOrDefault().IsNull()) return true; else throw new Exception(); }
            catch (Exception ex) { if (throwException) throw new PropertyNotAvailableException(ex); }

            return false;
        }

        public static bool CanWrite<T>(string propName, bool throwException)
        {
            if (HasProperty<T>(propName, throwException))
                return typeof(T).UnderlyingSystemType.GetProperty(propName).CanWrite;

            return false;
        }

        public static T SetValue<T>(T obj, string propName, object propValue)
        {
            if (obj.IsNull()) return obj;

            if (HasProperty<T>(propName, false))
            { obj.GetType().GetProperty(propName).SetValue(obj, propValue, null); return obj; }
            else return obj;
        }
        #endregion Properties

        #region Conversions
        #region Conversions : Array
        /// <summary>
        /// Converts Byte array to Image
        /// </summary>
        /// <param name="byt">Byte array</param>
        /// <returns>Image</returns>
        public static Image ConvertByteArrayToImage(byte[] byt)
        {
            MemoryStream ms = new MemoryStream(byt);
            Image returnImage = Image.FromStream(ms);
            return returnImage;
        }

        /// <summary>
        /// Converts Image to Byte array
        /// </summary>
        /// <param name="image">Image</param>
        /// <param name="format">Image format</param>
        /// <returns></returns>
        public static byte[] ConvertImageToByteArray(Image image, ImageFormat format)
        {
            MemoryStream ms = new MemoryStream();
            image.Save(ms, format);
            return ms.ToArray();
        }

        public static string ConvertByteArrayToString(byte[] value, bool removeEmptyCharacters)
        {
            byte[] res = value;
            if (removeEmptyCharacters)
                res = value.TakeWhile(b => b > 0).ToArray();

            return Encoding.Default.GetString(res);
        }

        public static byte[] ConvertStringToByteArray(string value, int length)
        {
            if (!value.IsNullOrEmpty())
                return value.ToCharArray().Select(ch => (byte)ch).ToArray();
            else
            {
                List<byte> byt = new List<byte>();
                for (int i = 0; i < length; i++) byt.Add(0);
                return byt.Cast<byte>().ToArray();
            }
        }
        #endregion Conversions : Array

        #region Conversions : Data
        public static DataTable ConvertIEnumerableToDataTable<TOutput>(IEnumerable<TOutput> value, DataTable dtOutput, string tableName = "")
            where TOutput : new()
        {
            bool isAllColumnAdded = true;
            typeof(TOutput).GetProperties().Select(prop => prop.Name).ForEach(propName =>
            {
                if (isAllColumnAdded)
                    if (!dtOutput.Columns.Cast<DataColumn>().Select(dcOutput => dcOutput.ColumnName).Contains(propName))
                        isAllColumnAdded = false;
            });

            if (dtOutput.IsNull() || !isAllColumnAdded)
                dtOutput = InitializeDataTableFromType<TOutput>(tableName);

            value.ForEach(op =>
            {
                DataRow drOutput = dtOutput.NewRow();
                op.GetType().GetProperties().ForEach(prop => drOutput[prop.Name] = prop.GetValue(op));
                dtOutput.Rows.Add(drOutput);
            });

            return dtOutput;
        }

        public static DataTable InitializeDataTableFromType<TOutput>(string tableName = "")
           where TOutput : new()
        {
            DataTable dtOutput = new DataTable(tableName);

            #region Options
            // OPTION - 1
            typeof(TOutput).GetProperties().ForEach(prop =>
            {
                if (!dtOutput.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName).Contains(prop.Name))
                    dtOutput.Columns.Add(prop.Name);
            });

            //// OPTION - 2
            //TOutput op = new TOutput();
            //op.GetType().GetProperties().ForEach(prop => dtOutput.Columns.Add(prop.Name));

            //// OPTION - 3
            //dtOutput.Columns.AddRange(typeof(TOutput).GetProperties().Select(prop => new DataColumn(prop.Name, prop.PropertyType)).ToArray());
            #endregion

            return dtOutput;
        }
        #endregion Conversions : Data
        #endregion Conversions

        #region Converter
        public static TOut ExtractConverterInputs<TOut>(object inputValue,
            TOut defaultValue = default(TOut), Func<TOut, bool> validation = null)
        {
            TOut padding = defaultValue;
            try { padding = inputValue.ChangeType<TOut>(); }
            catch (Exception) { }
            finally { if (!validation.IsNull()) if (validation(padding)) padding = defaultValue; }

            return padding;
        }
        #endregion Converter

        #region Controls
        /// <summary>
        /// Gets control name
        /// </summary>
        /// <param name="control">Control to be evaluated</param>
        /// <returns>Control name</returns>
        public static string GetControlName(object control)
        {
            Type T = control.GetType();
            string name = "";

            try { name = Convert.ToString(control.GetType().GetProperty("Name").GetValue(control, null)); }
            catch (Exception ex)
            { throw new InvalidControlTypeException(ex); }

            return name;
        }
        #endregion Controls

        #region UI
        /// <summary>
        /// Resizes fields against content
        /// </summary>
        /// <param name="txtTarget">Target control</param>
        /// <returns>Adjusted size (in pixels) of control</returns>
        public static Size AutoResizeTextBox(TextBox txtTarget)
        {
            return TextRenderer.MeasureText(txtTarget.Text, txtTarget.Font);
        }

        public static ContentAlignment GetOnlyLR(ContentAlignment align, ContentAlignment defaultAlign)
        {
            if (defaultAlign != Utilities.DefaultAlignmentLeft && defaultAlign != Utilities.DefaultAlignmentRight) defaultAlign = Utilities.DefaultAlignmentRight; // method default
            ContentAlignment? finalAlign = null;

            if (align.IsType<ContentAlignment>(eMatchType.Hierarchy))
            {
                switch (align)
                {
                    case ContentAlignment.BottomLeft:
                    case ContentAlignment.MiddleLeft:
                    case ContentAlignment.TopLeft:
                        finalAlign = Utilities.DefaultAlignmentLeft;
                        break;

                    case ContentAlignment.BottomRight:
                    case ContentAlignment.MiddleRight:
                    case ContentAlignment.TopRight:
                        finalAlign = Utilities.DefaultAlignmentRight;
                        break;

                    default:
                        finalAlign = defaultAlign;
                        break;
                }
            }

            return finalAlign.Value;
        }

        public static ContentAlignment InvertAlignment(ContentAlignment cont, ContentAlignment? defaultAlignment = null, eInversionMode? mode = null)
        {
            if (defaultAlignment.IsNull()) defaultAlignment = ContentAlignment.MiddleLeft;
            if (mode.IsNull()) mode = eInversionMode.Horizontal;
            ContentAlignment? target = null;

            if (mode == eInversionMode.Horizontal)
            {
                if (defaultAlignment.ToString().EndsWith("Center"))
                    defaultAlignment = GetEnumFromText<ContentAlignment>(
                        defaultAlignment.ToString().Replace("Center", "Left"));

                if (cont.ToString().Contains("Left"))
                    target = GetEnumFromText<ContentAlignment>(cont.ToString().Replace("Left", "Right"));
                else if (cont.ToString().Contains("Right"))
                    target = GetEnumFromText<ContentAlignment>(cont.ToString().Replace("Right", "Left"));
            }
            else if (mode == eInversionMode.Vertical)
            {
                if (cont.ToString().StartsWith("Bottom"))
                    target = GetEnumFromText<ContentAlignment>(cont.ToString().Replace("Bottom", "Top"));
                else if (cont.ToString().Contains("Top"))
                    target = GetEnumFromText<ContentAlignment>(cont.ToString().Replace("Top", "Bottom"));
            }

            return !target.IsNull() && target.HasValue
                ? target.Value : defaultAlignment.Value;
        }

        /// <summary>
        /// Selects the entire text in a control
        /// </summary>
        /// <param name="control">Control to perform on</param>
        /// <param name="modifierKeys">Indicates which of the modifier keys (SHIFT, CTRL, and ALT) is in pressed state</param>
        /// <param name="keyCode">Keyboard code for a System.Windows.Forms.Control.KeyDown or System.Windows.Forms.Control.KeyUp event</param>
        public static void SelectAllText(object control, Keys modifierKeys, Keys keyCode)
        {
            if (modifierKeys == Keys.Control && keyCode == Keys.A)
            {
                // SendKeys.Send("{Home}+{End}");

                #region System controls
                if (control.GetType() == typeof(TextBox))
                    ((TextBox)control).SelectAll();
                else if (control.GetType() == typeof(ComboBox))
                    ((ComboBox)control).SelectAll();
                else if (control.GetType() == typeof(RichTextBox))
                    ((RichTextBox)control).SelectAll();
                else if (control.GetType() == typeof(ToolStripTextBox))
                    ((ToolStripTextBox)control).SelectAll();
                #endregion

                #region Custom controls
                else if (control.GetType() == typeof(CUtil_TextBox))
                    ((CUtil_TextBox)control).SelectAll();
                else if (control.GetType() == typeof(CUtil_AddressTextBox))
                    ((CUtil_AddressTextBox)control).SelectAll();
                #endregion

                //else
                //{
                //    List<string> src_item = new List<string>();
                //    src_item.Add("BRTextBox"); src_item.Add("BRAdressTextBox");
                //
                //    if (src_item.Contains(control.GetType().Name))
                //    {
                //        ((TextBox)control).SelectAll();
                //    }
                //
                //    //if (control.GetType().Name == typeof(Controls.))
                //    //    ((TextBox)control).SelectAll();
                //    //else 
                //    //if (control.GetType() == typeof(TextBox))
                //    //    ((TextBox)control).SelectAll();
                //    //else 
                //}
            }
        }
        #endregion UI

        #region ToString methods
        // Utilities only
        public static string ToString()
        {
            return ToString(typeof(Utilities));
        }

        // Generic
        public static string ToString(object value)
        {
            return value.GetType().Assembly.FullName.ToString();
        }

        // Generic
        public static string ToString(Type t)
        {
            return ToString((object)t);
        }

        // Generic
        public static string ToString<T>()
        {
            return ToString(typeof(T));
        }
        #endregion ToString methods

        #region Utilities
        /// <summary>
        /// Swaps two values
        /// </summary>
        /// <param name="value1">First value</param>
        /// <param name="value2">Second value</param>
        public static void SwapValues(object value1, object value2)
        {
            object temp = value1;
            value1 = value2;
            value2 = temp;
        }

        public static long GetLineCount(object value)
        {
            return value.ToString().ConvertStringToLines().ToList().LongCount();
        }

        #region Date-Time
        public static DateTime GetDateTimeFromFormat(string dateTime, string format)
        {
            dateTime = dateTime.RemoveText("\0");

            #region Date
            // Year
            int year = 0;
            if (format.Contains("yyyy")) year = Convert.ToInt32(dateTime.Substring(format.IndexOf("yyyy"), "yyyy".Length));
            else if (format.Contains("yy")) year = Convert.ToInt32(dateTime.Substring(format.IndexOf("yy"), "yy".Length));
            else if (format.Contains("y")) /*year = Convert.ToInt32(dateTime.Substring(format.IndexOf("y"), "y".Length))*/;

            // Month
            int month = 0;
            if (format.Contains("MMMM"))
            {
                string sMonth = dateTime.Substring(format.IndexOf("MMMM"), "MMMM".Length);
                month = GetMonthIndexFromName(sMonth, "MMMM");
            }
            else if (format.Contains("MMM"))
            {
                string sMonth = dateTime.Substring(format.IndexOf("MMM"), "MMM".Length);
                month = GetMonthIndexFromName(sMonth, "MMM");
            }
            else if (format.Contains("MM")) month = Convert.ToInt32(dateTime.Substring(format.IndexOf("MM"), "MM".Length));
            else if (format.Contains("M")) /*month = Convert.ToInt32(dateTime.Substring(format.IndexOf("M"), "M".Length))*/;

            // Day
            int day = 0;
            if (format.Contains("dd")) day = Convert.ToInt32(dateTime.Substring(format.IndexOf("dd"), "dd".Length));
            else if (format.Contains("d")) day = Convert.ToInt32(dateTime.Substring(format.IndexOf("d"), "d".Length));
            #endregion

            #region Time
            // Hour
            int hour = 0;
            if (format.Contains("HH")) hour = Convert.ToInt32(dateTime.Substring(format.IndexOf("HH"), "HH".Length));
            else if (format.Contains("hh")) hour = Convert.ToInt32(dateTime.Substring(format.IndexOf("hh"), "hh".Length));
            else if (format.Contains("H")) /*hour = Convert.ToInt32(dateTime.Substring(format.IndexOf("H"), "H".Length))*/;
            else if (format.Contains("h")) /*hour = Convert.ToInt32(dateTime.Substring(format.IndexOf("h"), "h".Length))*/;

            // Minute
            int min = 0;
            if (format.Contains("mm")) min = Convert.ToInt32(dateTime.Substring(format.IndexOf("mm"), "mm".Length));
            else if (format.Contains("m")) /*min = Convert.ToInt32(dateTime.Substring(format.IndexOf("m"), "m".Length))*/;

            // Second
            int sec = 0;
            if (format.Contains("ss")) sec = Convert.ToInt32(dateTime.Substring(format.IndexOf("ss"), "ss".Length));
            else if (format.Contains("s")) /*sec = Convert.ToInt32(dateTime.Substring(format.IndexOf("s"), "s".Length))*/;
            #endregion

            DateTime dt = new DateTime(year, month, day, hour, min, sec, 0, DateTimeKind.Utc);
            return dt;
        }

        private static int GetMonthIndexFromName(string name, string format)
        {
            for (int iMonth = 1; iMonth <= 12; iMonth++)
                if (new DateTime(DateTime.Now.Year, iMonth, 1).ToString(format) == name)
                    return iMonth;

            return 0;
        }

        public static DateTime ConvertDateTimeToTimeSpan(TimeSpan timeSpan)
        {
            DateTime dt = new DateTime(timeSpan.Ticks);

            return dt;
        }

        public static string GetDuration(DateTime start, DateTime? stop = null, string format = "HH:mm:ss.ffffff")
        {
            TimeSpan timeSpan = stop.GetValueOrDefault(DateTime.Now) - start;
            DateTime dt = ConvertDateTimeToTimeSpan(timeSpan);

            return dt.ToString(format);
        }
        #endregion
        #endregion Utilities

        #region Suggestions
        // Actual logic
        public static string GetSuggestedPattern(IEnumerable<string> fileNames, eTrimDirection trimDirection, int suggPatternLen)
        {
            string pattern = "";
            ObservableCollection<string> patternList = fileNames
                ._Select(fnameOrg => fnameOrg.ToLower().TrimAndTake(suggPatternLen, trimDirection))
                .Distinct().ToObservableCollection();

            if (patternList.GetCount() == 1)
                pattern = "'{0}'".FormatTextFromParams(patternList.First());
            else
            {
                int iCounter = 0;
                int extraLen = 0;

                while (patternList._Select(fnameOrg => fnameOrg.ToLower().TrimAndTake(iCounter, trimDirection)).Distinct().GetCount() == 1)
                {
                    //if (patternList._Select(fnameOrg => fnameOrg.TrimAndTake(iCounter, trimDirection)).Distinct().GetCount() == 1)
                    extraLen = suggPatternLen - iCounter;
                    iCounter++;
                    //else
                    //{
                    //    int diff = pattern.Length - suggPatternLen;
                    //    pattern = patternList._Select(fnameOrg => fnameOrg.TrimAndTake(iCounter, trimDirection)).First();
                    //    pattern = "'{0}'".FormatTextFromParams(pattern) + ((diff < 0) ? " +{0}c".FormatTextFromParams(diff) : "");
                    //}
                }

                pattern = patternList.First().TrimAndTake(iCounter, trimDirection);
                pattern = "'{0}'".FormatTextFromParams(pattern) + ((extraLen > 0) ? " +{0}c".FormatTextFromParams(extraLen) : "");
            }

            return pattern;
        }

        /// <summary>
        /// RKD
        /// </summary>
        /// <param name="fileNames">List of file names (path removed, w/o extn.)</param>
        /// <param name="trimDirection">Direction to trim</param>
        /// <returns></returns>
        public static string GetSuggestedPattern(IEnumerable<string> fileNames, eTrimDirection trimDirection)
        {
            int suggPatternLen = 0;
            string pattern = "";

            if (AnalyzePattern(fileNames, trimDirection, ref suggPatternLen))
                pattern = GetSuggestedPattern(fileNames, trimDirection, suggPatternLen);

            return pattern;

            /*
            // Check pattern
            int iCounter = 1;
            int lenPattern = 0;

            //
            fileNames = fileNames.Select(fl => Path.GetFileNameWithoutExtension(fl.Trim()));

            do
            {
                if (fileNames.Select(fname => fname.TrimAndTake(iCounter, trimDirection)).Distinct().Count() == 1)
                {
                    lenPattern = iCounter;
                    iCounter++;
                }
            }
            while (fileNames.Select(fname => fname.TrimAndTake(iCounter, trimDirection)).Distinct().Count() == 1);

            fileNames = fileNames.Select(fname => fname.TrimAndTake(lenPattern, trimDirection)).Distinct();
            if (fileNames.Count() == 1) return fileNames.First(); else return String.Empty;
            */
        }

        public static bool GenerateFileNameSuggestion(IEnumerable<string> fileNameOrg, ref int suggLPatternLen, ref int suggRPatternLen)
        {
            //return AnalyzePatternLeft(fileNameOrg, ref suggLPatternLen) && AnalyzePatternRight(fileNameOrg, ref suggRPatternLen);

            return AnalyzePattern(fileNameOrg, eTrimDirection.Left, ref suggLPatternLen)
                && AnalyzePattern(fileNameOrg, eTrimDirection.Right, ref suggRPatternLen);
        }

        [Obsolete("Use generic method, instead", true)]
        public static bool AnalyzePatternLeft(IEnumerable<string> fileNameOrg, ref int suggLPatternLen)
        {
            return AnalyzePattern(fileNameOrg, eTrimDirection.Left, ref suggLPatternLen);

            /*
            int iLCounter = 1;

            try
            {
                do
                {
                    if (fileNameOrg.Select(fnameOrg => fnameOrg.TrimAndTake(iLCounter, eTrimDirection.Left)).Distinct().HasCount(1))
                    {
                        suggLPatternLen = iLCounter;
                        iLCounter++;
                    }
                }
                while (fileNameOrg.Select(fnameOrg => fnameOrg.TrimAndTake(iLCounter, eTrimDirection.Left)).Distinct().HasCount(1));

                return true;
            }
            catch (Exception)
            {
                return false;
            }
            */
        }

        [Obsolete("Use generic method, instead", true)]
        public static bool AnalyzePatternRight(IEnumerable<string> fileNameOrg, ref int suggRPatternLen)
        {
            return AnalyzePattern(fileNameOrg, eTrimDirection.Right, ref suggRPatternLen);

            /*
            int iRCounter = 1;

            try
            {
                do
                {
                    if (fileNameOrg.Select(fnameOrg => fnameOrg.TrimAndTake(iRCounter, eTrimDirection.Right)).Distinct().HasCount(1))
                    {
                        suggRPatternLen = iRCounter;
                        iRCounter++;
                    }
                }
                while (fileNameOrg.Select(fnameOrg => fnameOrg.TrimAndTake(iRCounter, eTrimDirection.Right)).Distinct().HasCount(1));

                return true;
            }
            catch (Exception)
            {
                return false;
            }
            */
        }

        public static bool AnalyzePattern(IEnumerable<string> fileNameOrg, eTrimDirection trimDirection, ref int suggPatternLen)
        {
            int iCounter = 1;

            try
            {
                while (fileNameOrg.Select(fnameOrg => fnameOrg.ToLower().TrimAndTake(iCounter, trimDirection)).Distinct().HasCount(1))
                {
                    suggPatternLen = iCounter;
                    iCounter++;
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        #endregion Suggestions

        #region Attributes
        // RKD : Move to Utilities under Attributes region
        // Param-less overload method
        // RKD : Test PENDING
        public static TAttrib GetAttribute<TObject, TAttrib>()
            where TObject : class, new()
            where TAttrib : Attribute
        {
            TObject value = new TObject { };
            return value.GetAttribute<TObject, TAttrib>();
        }

        // RKD : Move to Utilities under Attributes region
        // Param-less overload method
        // RKD : Test PENDING
        public static string GetClassDescription<TClass>(params object[] param)
           where TClass : class, new()
        {
            TClass value = new TClass { };
            return value.GetClassDescription<TClass>(param); // .FormatTextFromParams(param);
        }

        // RKD : Move to Utilities under Attributes region
        // Param-less overload method
        // RKD : Test PENDING
        public static object GetPropertyAttribute<TClass, TAttrib>(string propertyName)
            where TClass : class, new()
            where TAttrib : Attribute, new()
        {
            TClass value = new TClass { };
            return value.GetPropertyAttribute<TClass, TAttrib>(propertyName);
        }
        #endregion Attributes

        #region TimeSpan
        /// <summary>
        /// Formats time span based on start and end times
        /// <para>(Default format if skipped: &quot;ss.fff&quot;)</para>
        /// </summary>
        /// <param param name="start">Start time</param>
        /// <param name="end">End time</param>
        /// <param name="format">Format: [-][d.]hh:mm:ss[.fffffff]
        /// <para>"-"         : A minus sign, which indicates a negative time interval. No sign is included for a positive time span.</para>
        /// <para>"d"         : The number of days in the time interval. This element is omitted if the time interval is less than one day.</para>
        /// <para>"hh"        : The number of hours in the time interval, ranging from 0 to 23.</para>
        /// <para>"mm"        : The number of minutes in the time interval, ranging from 0 to 59.</para>
        /// <para>"ss"        : The number of seconds in the time interval, ranging from 0 to 59.</para>
        /// <para>"fffffff"   : Fractional seconds in the time interval. This element is omitted if the time interval does not include fractional seconds.</para>
        ///                     <para>If present, fractional seconds are always expressed using seven decimal digits.</para></param>
        /// <returns></returns>
        public static string FormatTimeSpan(DateTime start, DateTime end, string format)
        {
            return (end - start).FormatTimeSpan();
        }
        #endregion TimeSpan

        public static bool HasDefaultCtor(Type TClass)
        {
            return TClass.UnderlyingSystemType.GetConstructors()._Where(ctor => ctor.GetParameters().HasCount(0))._Any();
        }

        public static bool HasDefaultCtor<TClass>()
        {
            return HasDefaultCtor(typeof(TClass));
        }

        public static string GenerateDebugInfo(StackFrame frame)
        {
            MethodBase methodBase = frame.GetMethod();
            string lineInfo = frame.GetFileName()
                + (frame.GetFileLineNumber() + frame.GetFileColumnNumber() == 0 ? "" : ": "
                        + (frame.GetFileLineNumber() == 0 ? "--" : frame.GetFileLineNumber().ToString()) + ","
                        + (frame.GetFileColumnNumber() == 0 ? "--" : frame.GetFileColumnNumber().ToString())
                  );

            return methodBase.DeclaringType.FullName + " : " + methodBase.Name + (lineInfo.IsNullOrEmpty() ? "" : " (" + lineInfo + ")");
        }

        public static string CapitalizeThisWord(this string word)
        {
            string result = String.Empty;
            foreach (char chr in word.ToLower().ToCharArray())
                result += String.IsNullOrEmpty(result) ? chr.ToString().ToUpper() : chr.ToString();

            return result.Trim();
        }

        public static string CapitalizeEachWord(this string sentence, params string[] skip)
        {
            string result = String.Empty;
            foreach (string word in sentence.Split(' '))
                result += (skip.Contains(word) ? word : word.CapitalizeThisWord()) + " ";

            return result.Trim();
        }

        #region WIP :: Work in Progress
        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <seealso cref="http://msdn.microsoft.com/en-us/library/system.reflection.bindingflags.aspx" />
        public static MethodBase[] GetMethods(Type type)
        {
            return type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
        }

        // base path can/cannot? contain file name :: being developed in RLUtilities - RKD

        // BREAK

        /// <summary>
        /// 
        /// </summary>
        /// <param name="profilePath"></param>
        /// <returns></returns>
        /// <seealso cref="http://www.vbforums.com/showthread.php?586974-Create-a-shortcut-using-IWshRuntimeLibrary" />
        /// <seealso cref="http://www.codeproject.com/Articles/3905/Creating-Shell-Links-Shortcuts-in-NET-Programs-Usi" />
        public static bool CreateProfileShortcut(string profilePath)
        {
            bool success = false;
            string favoritesPath = "";
            string profileName = AppInfo.AssemblyTitle;
            IWshRuntimeLibrary.IWshShell wsh;
            IWshRuntimeLibrary.IWshShortcut shortcut;

            try
            {
                if (!String.IsNullOrWhiteSpace(profilePath) && Directory.Exists(profilePath))
                {
                    favoritesPath = Environment.SpecialFolder.UserProfile.GetFolderPath();
                    if (!(favoritesPath + "\\Links\\" + profileName + ".lnk").ValidatePath(ePathValidation.File))
                    {
                        wsh = new IWshRuntimeLibrary.WshShell();
                        shortcut = (IWshRuntimeLibrary.IWshShortcut)wsh.CreateShortcut(favoritesPath + @"\Links\" + profileName + ".lnk");
                        shortcut.TargetPath = AppInfo.AssemblyLocation;
                        shortcut.WindowStyle = 1;
                        shortcut.Description = AppInfo.AssemblyDescription;
                        shortcut.WorkingDirectory = Environment.SpecialFolder.Desktop.GetFolderPath(); // SpecialDirectories.Desktop;
                        shortcut.IconLocation = AppInfo.AssemblyLocation + @"\" + profileName + ".ico";
                        shortcut.Save();
                    }
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(String.Concat(e.TargetSite.Name, e.Message, "MinorError"));
                success = false;
            }

            return success;
        }

        // RKD
        public static Cursor LoadCustomCursor(string path)
        {
            IntPtr hCurs = LoadCursorFromFile(path);
            if (hCurs == IntPtr.Zero) throw new Win32Exception();
            var curs = new Cursor(hCurs);
            // Note: force the cursor to own the handle so it gets released properly
            var fi = typeof(Cursor).GetField("ownHandle", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(curs, true);
            return curs;
        }
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr LoadCursorFromFile(string path);

        // RKD
        //public IQueryable<T> SelectAll<T>(IEnumerable<T> expression, List<Expression<Func<T, bool>>> whereCls)
        //{
        //    IQueryable<T> selectAllQry = new Queryable<T>();
        //
        //    if (!whereCls.IsNull())
        //    {
        //        foreach (Expression<Func<T, bool>> whereStmt in whereCls)
        //        {
        //            selectAllQry = expression.Where<T>(whereStmt);
        //        }
        //    }
        //
        //    return selectAllQry;
        //}
        //
        //public static void LoadStyles()
        //{
        //    // Labels
        //    //StyleGuide.Label_FontFamily = "Optima";
        //    //StyleGuide.Label_FontSize = 9F;
        //    //StyleGuide.Label_FontStyle = FontStyle.Bold;
        //
        //    // BRButton
        //    // code_here // RKD
        //
        //    // TextBox/BRTextBox
        //    //StyleGuide.BRTextBox_MouseEnterFontStyle = FontStyle.Underline;
        //
        //    // MenuStrip
        //    //StyleGuide.MenuStrip_BackColour = Color.LemonChiffon;
        //
        //    // Menu
        //    //StyleGuide.Menu_FontFamily = "Microsoft Sans Serif";
        //    //StyleGuide.Menu_FontSize = 6.75F;
        //    //StyleGuide.Menu_FontStyle = FontStyle.Bold;
        //
        //    // NumericUpDown
        //    // code_here // RKD
        //
        //    // etc.
        //    // code_here // RKD
        //
        //    //this.grpEditor.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        //    //this.btnUpdateDetails.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        //    //this.txtNameOrg.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        //    //this.numLTrim.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        //    //this.grpOutput.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        //    //this.tltpMain.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info;
        //    //this.mnuBulkRenamer.Font = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        //} 
        #endregion WIP :: Work in Progress
        #endregion Methods
    }
}
